class EnemyCharacter extends MonoBehaviour
{
	//References
	protected var _mapReference:Map;
	protected var _characterManager:CharacterManager;
	public var _tileReference:Tile;
	protected var _nearestCoverTile:Tile;
	
	//Turns
	protected var MAX_TURNS:int = 2;
	protected var turnsLeft:int; //decision number. each unit is granted 2 moves. some moves are restricted in second turn
	
	protected var myHealth:int = 100;
	protected var isDead:boolean = false;
	protected var deadAnimationFinished:boolean = false;
	
	//Hovering GUI
	protected var maxHealth:int = 100; 
	protected var screenPosition:Vector3; 
	protected var healthBarLength:float; 
	protected var _healthPercent:float;
	protected var style:GUIStyle;
	protected var backStyle:GUIStyle;
	protected var texture:Texture2D;
	protected var backTexture:Texture2D;
	 
	//Label
	protected var fontstyle:GUIStyle;
	protected var toDisplayOverlayText :boolean;
	
	//Name of character
	protected var characterName:String;
	
	//Animation
	protected var isExecuting:boolean;		
	protected var isPlayerCharacter:boolean; //set is player or AI
	
	//Sight Radius
	protected var sightRadius:int = 15;
	protected var moveRadius:int = 15;	
	
	//Attack radius
	protected var attackRadius;
	protected var engageRadius = 0;
	protected var skillRadius = 0;
	protected var attackDamage = 10;
	protected var actualDamage = 10;
	
	//whether the character moved
	protected var _hasMoved:boolean;
	protected var _path:Array;
	protected var _destTile:Tile;
	protected var _destPos:Vector3;
	protected var _destReached:boolean;
	protected var _directionV:Vector3;
	protected var _speedMultiplier:float;
	
	//Flanking players
	protected var previousFlankable:Array;
	
	//Previous tile
	protected var _previousTile:Tile;
	
	//AI State
	enum DecisionState { ATTACK, HEAL }
	protected var _decisionState:DecisionState;
	
	//Visibility
	public var _visible:boolean;
	
	protected var _isSelectable:boolean;
	protected var _attackableText:GameObject;

	protected var _isExposed:boolean;
	
	//Lock down variable
	protected var _isLocked:boolean;
	
	//Attack threshold
	protected var _attackPercent:double;
	
	//Skills
	protected var _maxSkillCooldown:int;
	protected var _skillCooldown:int;
	protected var _isCooldown:boolean;
	protected var _aoeGrid:GameObject;
	
	//Enemy type
	enum EnemyType { ENEMY_MELEE, ENEMY_ARCHER, ENEMY_HEALER, ENEMY_SPELL }
	protected var _enemyType:EnemyType;
		
	//Animation
	protected var STOP_ANI:int = 0;
	protected var WALK_ANI:int = 1;
	protected var JUMP_ANI:int = 2;
	protected var SKILL_ANI:int = 3;
	protected var COVER_IN_ANI:int = 4;
	protected var ATTACK_ANI:int = 5;
	public var _currentAniState:int = STOP_ANI;
	
	protected var walk_model:GameObject;
	protected var jump_model:GameObject;
	protected var skill_model:GameObject;
	protected var attack_model:GameObject;
	protected var cover_in_model:GameObject;
	
	//Peekaboo
	protected var _peekabooTarget:GameCharacter;
	protected var _peekabooEnabled:boolean;
	protected var _peekabooCount:int;
	protected var _peekabooLock:boolean;
	
	//Sounds
	protected var walkSound:AudioClip;
	protected var jumpSound:AudioClip;
	protected var bowSound:AudioClip;
	protected var WALK_VOLUME:float = 0.2f;
	
	//Texture
	protected var coverTexture:Texture2D;
	private var customGuiStyle : GUIStyle;
	
	public function EnemyCharacter()
	{	
		isDead = false;	
		toDisplayOverlayText = false;		
		
		turnsLeft = MAX_TURNS;
		previousFlankable = new Array(0);
		_decisionState = DecisionState.ATTACK;
		_previousTile = _tileReference;
		
		isExecuting = false;
		_peekabooEnabled = false;
		_peekabooLock = false;
		_peekabooCount = 0;
	
		_visible = false;
		_isExposed = false;
		
		_hasMoved = true;
		_path = new Array(0);
		_destReached = true;
		_destTile = null;
		characterName = "Enemy";
		_isLocked = false;
		
		//Skills
		_maxSkillCooldown = 2;
		_skillCooldown = 0;
		_isCooldown = false;
		
		//For diplaying mouse over
		_healthPercent = 100.0f;
		healthBarLength = Screen.width/9;
		style = new GUIStyle();
	    texture = new Texture2D(1, 1);
		backTexture = new Texture2D(1, 1);
			
    	for (var y:int = 0; y < texture.height; ++y)
	    {
	    	for (var x:int = 0; x < texture.width; ++x)
	        {
	            backTexture.SetPixel(x, y, Color.gray);
	        }
	    }
    	backTexture.Apply();
    	backStyle = new GUIStyle();    
    	backStyle.normal.background = backTexture;		
		
		_isSelectable = false;
		_attackableText = null;
		
		//Load sounds
		walkSound = Resources.Load("walkSound");	
		jumpSound = Resources.Load("jumpSound");
		bowSound = Resources.Load("bowSound");
		
		coverTexture = Resources.Load("cover");
		
		var myFont:Font = Resources.Load("PineappleDemo");
		customGuiStyle = new GUIStyle();
 		customGuiStyle.fontSize = 15;
 		customGuiStyle.font = myFont;
 		customGuiStyle.normal.textColor = Color.white;
	}
	
	public function initAnimation()
	{
		walk_model = this.transform.Find("walk").gameObject;
		jump_model = this.transform.Find("jump").gameObject;
		skill_model = this.transform.Find("skill").gameObject;
		attack_model = this.transform.Find("attack").gameObject;
		cover_in_model = this.transform.Find("toCover").gameObject;
		setAnimationState(WALK_ANI);
		setAnimationState(STOP_ANI);
		setIsVisible(false);
	}
	
	//OTHER FUNCTIONS
	public function setProjectileReached(reached:boolean)
	{	
		if(reached){
			_characterManager.setMoveDone(true);
			_isLocked = false;
		}
	}
	
	//used by CharacterManager to reset the character
	public function resetTurn()
	{
		_isLocked = false;
		turnsLeft = MAX_TURNS;
		previousFlankable = new Array(0);
		_previousTile = _tileReference;
	}
	//used locally to end this character's turn and tell CharacterManager
	public function endTurn()
	{
		turnsLeft = 0;
		
		if(_skillCooldown > 0){
			_skillCooldown--;
		}else if(_skillCooldown == 0){
			_isCooldown = false;
		}
		
		yield WaitForSeconds(3.0);
		if (_tileReference.isCover())
			setAnimationState(COVER_IN_ANI);
		//_characterManager.enemyEnd();
		
	}
	
	public function setSelectable(selectable:boolean, text)
	{
		_isSelectable = selectable;
		if (_isSelectable)
		{
			_attackableText = Resources.Load("Attackable_text");
			_attackableText = MonoBehaviour.Instantiate(_attackableText);
			_attackableText.transform.position = _tileReference.getSurfaceCoord();
			_attackableText.transform.position.y += 2;
			_attackableText.GetComponent(AttackableText).setText(text, 70, 16, Color.white);					
		}
		else
		{
			Destroy(_attackableText);
			_attackableText = null;
		}	
	}	
	
	public function setSelectable(selectable:boolean, hitChance:float, flanked:boolean)
	{
		_isSelectable = selectable;
		if (_isSelectable)
		{
			_attackableText = Resources.Load("Attackable_text");
			_attackableText = MonoBehaviour.Instantiate(_attackableText);
			_attackableText.transform.position = _tileReference.getSurfaceCoord();
			_attackableText.transform.position.y += 2;			
			var text = "Hit Chance\n " + hitChance.ToString() + "%";
			
			if (flanked)
				text = text+" "+"\nFlanked!";	
				
			_attackableText.GetComponent(AttackableText).setText(text, 70, 16, Color.white);					
		}
		else
		{
			Destroy(_attackableText);
			_attackableText = null;
		}	
	}
	
	public function resetSelectable(){
		_isSelectable = false;
		Destroy(_attackableText);
		_attackableText = null;
	}
	
	public function isSelectable()
	{
		return _isSelectable;
	}

	public function attack(target:GameCharacter, projectileType:int)
	{
		//calculate actual atk damage
		actualDamage = calculateActualDamage(target);	
				
		audio.PlayClipAtPoint(bowSound,Vector3(0,0,0));
		
		//rotate to face the target
		var directionOfAttack:Vector3 = target.getSurfaceCoord() - this.getSurfaceCoord();
		directionOfAttack.y = 0;
		this.transform.rotation = Quaternion.LookRotation(directionOfAttack);
	}
	
	//OVERRIDE THIS FUNCTION IN EVERY INHERITED CLASS
	public function calculateHitChance(target:GameCharacter)
	{
		//currently calculating based on an average atk
		//calculate distance of enemy
		var distance = Vector3.Distance(target.getSurfaceCoord(), this.getSurfaceCoord());
		//based on the maximum range of weapon, calculate the hit chance based on the optimized range
		//close - mid range weapon
		var optimizedRangeMax = 5;
		var hitChance = 100;
		//too far
		if (distance > optimizedRangeMax)
			hitChance -= (distance - optimizedRangeMax) * 20;
		//if in cover
		if (target.isInCover())
		{
			if (!target.isFlanked(this))
				hitChance -= Obstacle.HIT_CHANCE_REDUCTION;
		}
		
		//check height advantage
		var heightDifference = target.getSurfaceCoord().y - this.getSurfaceCoord().y;
		if (heightDifference > 0)
			hitChance -= heightDifference * 10;
		else if (heightDifference < 0)
			hitChance += heightDifference * -10;
		
		//minimum 10%
		if (hitChance < 10)
			hitChance = 10;
		else if (hitChance > 100)
			hitChance = 100;
		return hitChance;
	}
	
	public function calculateActualDamage(target:GameCharacter)
	{
		//if in cover
		if (target.isInCover())
		{
			//check whether flanked
			if (target.isFlanked(this))
			{
				return attackDamage *2; 
			}
			else
			{
				return attackDamage;
			}
		}
		//not in cover
		else
		{
			return attackDamage *2;
		}
	}	
	
	public function skill(target:GameCharacter) //even fire ball must aim for person
	{		
	}
	
	public function skill(target:EnemyCharacter) //even fire ball must aim for person
	{			
	}

	protected function startCooldown(){
		_isCooldown = true;
		_skillCooldown = _maxSkillCooldown;
	}
	
	protected function increaseHealthPercent(healthPercent:int){
		var health = Mathf.Floor((healthPercent/100.0)*maxHealth);
		
		adjustCurrentHealth(health);
		
		toDisplayOverlayText = true;
		
		yield WaitForSeconds(3.0);
		
		toDisplayOverlayText = false;
	}
	
	//UPDATE FUNCTIONS
	function Update () {

		//skip update if dead 
		if (isDead)
		{
			if (deadAnimationFinished)
				return;
			else
			{
				this.transform.position.y -= 0.003;
				if (this.transform.position.y < -2)
					deadAnimationFinished = true;
				return;
			}
		}
	
		//if not at destination
		if (!_destReached && !_peekabooLock)
		{
			var currPos:Vector3 = this.transform.position;
			//Debug.Log("Distance Travelled: "+Vector3.Distance(currPos, _destPos));
			if (Vector3.Distance(currPos, _destPos) <= 0.1)
			{
				//reached destination
				if (_path.length != 0)
				{					
					_destTile = _path.Shift();
					_destPos = _destTile.getSurfaceCoord();
					_destPos.y += this.renderer.bounds.size.y/2;
				
					//not reached yet. move towards it
					_directionV = _destPos - currPos;
					_directionV.Normalize();
			
					//rotate to face the target
					//remove the rotation for height
					//play animation
					setIsVisible(_destTile.getIsVisible());
					var lookRotation:Vector3 = new Vector3(_directionV.x, 0, _directionV.z);
					this.transform.rotation = Quaternion.LookRotation(lookRotation);	
					
					if (_destTile.getHeight() == _tileReference.getHeight())
					{
						audio.PlayClipAtPoint(walkSound,Vector3(0,0,0), WALK_VOLUME);
						setAnimationState(WALK_ANI);
						_speedMultiplier = 0.05;
					}
					else
					{
						audio.PlayClipAtPoint(jumpSound,Vector3(0,0,0));
						setAnimationState(JUMP_ANI);
						_speedMultiplier = 0.025;
					}
				}
				else
				{
					Debug.Log("reached destination");
					//reduce turn after move has finihsed
					turnsLeft--;
					if (turnsLeft <= 0)
						endTurn();
					_destReached = true;
					_isLocked = false;

					_characterManager.setMoveDone(true);
					
					_destTile.setIsMovingOver(false);
					
					//play animation
					setIsVisible(_destTile.getIsVisible());
					//play animation
					if (_destTile.isCover())
						setAnimationState(COVER_IN_ANI);
					else
						setAnimationState(STOP_ANI);
				}
				_hasMoved = true;
				
				this.setWorldCoord();
				peekabooCheck();
				_tileReference.clearEnemy();
				this.setTileReference(_destTile);	
			}
			else
			{
				this.transform.position += _directionV * _speedMultiplier;
				
			}
		}
	}
	
	function OnGUI () {
    	//disable GUI if dead
		if( isDead )
			return;
    
		if( toDisplayOverlayText && _visible)
		{
			//calculate rotation for GUI to face camera
			screenPosition = Camera.main.WorldToScreenPoint(transform.position);
    		screenPosition.y = Screen.height - (screenPosition.y + 1); 
			//Display enemy attributes
    		GUI.Label(Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 94,100,50), characterName, customGuiStyle);//,fontstyle);
			GUI.Label(Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 80,100,50), turnsLeft+" moves", customGuiStyle);//,fontstyle);			
			GUI.Label(Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 55,100,50), myHealth+"/"+maxHealth, customGuiStyle);//fontstyle);	
			
			//show whether tile is cover
			if(_tileReference.isCover())
				GUI.DrawTexture(Rect(screenPosition.x - (healthBarLength/2) - 35, screenPosition.y - 85, 30, 30), coverTexture); 
			
			displayHealthBar();
			
			if (_aoeGrid != null && !_aoeGrid.active)
			{
				_aoeGrid.SetActive(true);
			}
		}

	}
	
	//MOVING FUNCTIONS
	public function moveTo(tile:Tile):void{
		_path = _mapReference.getPath(tile.getCoord());
		_path.Shift();
		_destTile = _path.Shift();
		_destPos = _destTile.getSurfaceCoord();
		_destPos.y += this.renderer.bounds.size.y/2;
		
		_directionV = _destPos - this.transform.position;
		_directionV.Normalize();
		//remove the rotation for height
		//rotate to face the target
		var lookRotation:Vector3 = new Vector3(_directionV.x, 0, _directionV.z);
		this.transform.rotation = Quaternion.LookRotation(lookRotation);
		_destReached = false;
		
		setIsVisible(_destTile.getIsVisible());
		_tileReference.clearEnemy();
		this.setTileReference(_destTile);
		
		//play animation
		if (_directionV.y == 0)
		{
			audio.PlayClipAtPoint(walkSound,Vector3(0,0,0), WALK_VOLUME);		
			setAnimationState(WALK_ANI);
			_speedMultiplier = 0.05;
		}
		else
		{
			audio.PlayClipAtPoint(jumpSound,Vector3(0,0,0));		
			setAnimationState(JUMP_ANI);
			_speedMultiplier = 0.025;
		}
	}
	public function isWithinMoveRange(tile:Tile)
	{
		return _mapReference.checkReachable(tile.getCoord());
	}	

	//MOUSE FUNCTIONS
	function OnMouseOver () {
	}
	function OnMouseExit () {
		toDisplayOverlayText = false;
		if (_aoeGrid != null && _aoeGrid.active)
			_aoeGrid.SetActive(false);
	}
	
	function OnMouseEnter(){
		toDisplayOverlayText = true;
		_tileReference.highlightMe();
	}
	
	function OnMouseDown()
	{
		if (_isSelectable)
			_characterManager.setSelectedEnemy(this);
	}
	
	
	
	//visibility functions
	//used mostly by the map to set enemy in the Fog invisible
	function setIsVisible(b:boolean):void
	{
		_visible = b; 
		var renderers;
		if (_visible)
		{
			if(!_isExposed){
				_isExposed = true;
				
				// when exposed, move to nearest cover
				_mapReference.checkRangeFrom(getCoord(), moveRadius);
				var coverList:Array = _mapReference.getCoverTiles(getCoord(), moveRadius);
				
				var closestCoverDistance = 9999999;
				var currentDistance;
				
				// Set to run to closest tile by default
				var closestCover:Tile = null;	
				
				for(var i:int=0; i<coverList.length; i++){
					var current_tile:Tile = coverList[i];				
					currentDistance = Vector2.Distance(current_tile.getCoord(), getCoord());
					if(currentDistance < closestCoverDistance && !current_tile.isMovingOver()){							
						closestCoverDistance = currentDistance;
						closestCover = current_tile;
					}							
				}
				
				if(closestCover != null){
					closestCover.setIsMovingOver(true);
					
					moveTo(closestCover);
				}
			}
			
			renderers = GetComponentsInChildren(Renderer);
			for (var r : Renderer in renderers)
    			r.enabled = true;
			gameObject.renderer.enabled = true;
		}
		else
		{	
			renderers = GetComponentsInChildren(Renderer);
			for (var r : Renderer in renderers)
    			r.enabled = false;
			gameObject.renderer.enabled = false;
		}
	}
	
	function isVisible():boolean { return _visible; }
	function isExposed():boolean { return _isExposed; }
	
	//PERSONAL GUI FUNCTIONS
	function displayHealthBar(){					
    	var oldColor:Color = GUI.color;
    	var newColor:Color = Color.green;
    	   	
    	if(_healthPercent < 0.2){
   			newColor = Color.red;
   		}else if(_healthPercent < 0.5){
   			newColor = Color.yellow;
   		}
   		
	  
	    for (var y:int = 0; y < texture.height; ++y)
	    {
	    	for (var x:int = 0; x < texture.width; ++x)
	        {
	            texture.SetPixel(x, y, newColor);
	        }
	    }
	    texture.Apply(); 
    	style.normal.background = texture; 	
    	
    	GUI.Box(new Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 65, (Screen.width / 9), 7), "", backStyle);
    	
   		GUI.Box(new Rect(screenPosition.x - (healthBarLength/2), screenPosition.y - 65, healthBarLength, 7), "", style);

   		GUI.color = oldColor;   		  		
	}
	
	function adjustCurrentHealth(adj:int) {
       	
       	if(adj < 0){
       		var reduceHealthText = Resources.Load("Attackable_text");
			reduceHealthText = MonoBehaviour.Instantiate(reduceHealthText);
			reduceHealthText.transform.position = _tileReference.getSurfaceCoord();
			reduceHealthText.transform.position.y += 2;
			reduceHealthText.GetComponent(AttackableText).setAutoRemoveText(adj.ToString(), 35, 3);
       	}else if(adj > 0){
       		var increaseHealthText = Resources.Load("Attackable_text");
			increaseHealthText = MonoBehaviour.Instantiate(increaseHealthText);
			increaseHealthText.transform.position = _tileReference.getSurfaceCoord();
			increaseHealthText.transform.position.y += 2;
			increaseHealthText.GetComponent(AttackableText).setAutoRemoveText("+ "+adj.ToString(), 35, 3, Color.green);
       	}
        
        myHealth += adj;
 		
        if(myHealth <= 0)
        {
            //Destroy(gameObject);
            isDead = true;
			_tileReference.clearEnemy();
        }
 
        if(myHealth > maxHealth)
            myHealth = maxHealth;
        if(maxHealth < 1)
            maxHealth = 1;
            
        _healthPercent = (myHealth / (maxHealth*1.0));
        healthBarLength = (Screen.width / 9) * _healthPercent;
    }
	//PERSONAL GUI END
	
	protected function setVisibleState(visible:boolean)
	{
		if (visible)
		{
			switch (_currentAniState)
			{
				case WALK_ANI:
					walk_model.SetActive(true);
					break;
					
				case JUMP_ANI:
					jump_model.SetActive(true);
					break;
				
				case SKILL_ANI:
					skill_model.SetActive(true);
					break;
				
				case ATTACK_ANI:
					attack_model.SetActive(true);
					break;
				
				case COVER_IN_ANI:
					cover_in_model.SetActive(true);
					break;
			}	
		}
		else
		{
			jump_model.SetActive(false);
			skill_model.SetActive(false);
			attack_model.SetActive(false);
			cover_in_model.SetActive(false);
			walk_model.SetActive(false);
		}	
	}
	
	protected function setAnimationState(state:int)
	{
		if (_visible)
		{
			switch (state)
			{
				case STOP_ANI:
					switch (_currentAniState)
					{
						case JUMP_ANI:
							//don't stop any animation
							break;
						default:
							walk_model.animation.Stop();
							jump_model.animation.Stop();
							skill_model.animation.Stop();
							attack_model.animation.Stop();
							cover_in_model.animation.Stop();
					}
					break;
					
				case WALK_ANI:
					jump_model.SetActive(false);
					skill_model.SetActive(false);
					attack_model.SetActive(false);
					cover_in_model.SetActive(false);
					walk_model.SetActive(true);
					walk_model.animation.Play();
					break;
					
				case JUMP_ANI:
					walk_model.SetActive(false);
					skill_model.SetActive(false);
					attack_model.SetActive(false);
					cover_in_model.SetActive(false);
					jump_model.SetActive(true);
					jump_model.animation.Play();
					break;
					
				case SKILL_ANI:
					walk_model.SetActive(false);
					jump_model.SetActive(false);
					attack_model.SetActive(false);
					cover_in_model.SetActive(false);
					skill_model.SetActive(true);
					skill_model.animation.Play();
					break;
					
				case ATTACK_ANI:
					walk_model.SetActive(false);
					jump_model.SetActive(false);
					skill_model.SetActive(false);
					cover_in_model.SetActive(false);
					attack_model.SetActive(true);
					attack_model.animation.Play();
					break;
				
				case COVER_IN_ANI:
					walk_model.SetActive(false);
					jump_model.SetActive(false);
					skill_model.SetActive(false);
					attack_model.SetActive(false);
					if (_currentAniState != COVER_IN_ANI)
					{
						cover_in_model.SetActive(true);
						cover_in_model.animation.Play();
					}
					break;
			}
		}
		if (state != STOP_ANI)
			_currentAniState = state;
	}
	
	//PEEKABOO FUNCTIONS
	public function peekabooDone()
	{	
		//shift back to cover animation if its originally in cover
		if (_tileReference.isCover())
			setAnimationState(COVER_IN_ANI);
				
		//unlock target so that it can continue movement
		_peekabooTarget.peekabooLock(false);
			
		
	}
	
	public function peekabooLock(b:boolean)
	{	
		if(b)
			_peekabooCount++;
		else
			_peekabooCount--;
			
		if (_peekabooCount > 0)
			_peekabooLock = true;
		else
			_peekabooLock = false;

	}
	
	public function peekabooAttack(target:GameCharacter)
	{
		Debug.Log("PEEKABOO");
		_peekabooTarget = target;
		//fire projectile
				
		audio.PlayClipAtPoint(bowSound,Vector3(0,0,0));
		setAnimationState(ATTACK_ANI);
		attack(target, Projectile.PEEKABOO);
		
		//clear peekaboo state
		_peekabooEnabled = false;
		
		//put 3d text PEEKABOO
	}
	
	protected function peekabooCheck()
	{
		var peekabooedPlayers:Array = _characterManager.getPlayerPeekabooList();
		
		//loop through all and calculate distance 
		for (var i:int = 0; i < peekabooedPlayers.length; i++)
		{
			if (!peekabooedPlayers[i].isPeekabooEnabled())
				continue;
			var currPlayerCoord = peekabooedPlayers[i].getSurfaceCoord();
			var currPlayerAtkRadius = peekabooedPlayers[i].getAttackRadius();
			
			//calculate the distance
			var euclideanDistance = currPlayerCoord - this.getSurfaceCoord();
			var distance = euclideanDistance.x * euclideanDistance.x + euclideanDistance.y * euclideanDistance.y + euclideanDistance.z * euclideanDistance.z;
			//within attack radius
			if (distance <= currPlayerAtkRadius*currPlayerAtkRadius)
			{
				//check ray cast to see if within LoS
				var raySource:Vector3 = peekabooedPlayers[i].getSurfaceCoord();
				this.gameObject.layer = LayerMask.NameToLayer("Obstacle");
				var rayDst:Vector3 = this.getSurfaceCoord();
				var hit:RaycastHit;
				if (Physics.Raycast(raySource, rayDst - raySource, hit, currPlayerAtkRadius, 1<<LayerMask.NameToLayer("Obstacle")))
				{
					if (hit.collider.gameObject.tag.Equals("EnemyCharacter"))
					{
						peekabooedPlayers[i].peekabooAttack(this);
						peekabooLock(true);
					}
				}
				this.gameObject.layer = LayerMask.NameToLayer("Enemy");
			}
		}
	}
	
	public function setPeekabooEnabled(enabled:boolean) { _peekabooEnabled = true; }
	public function isPeekabooEnabled() { return _peekabooEnabled; }
	

	//GETTERS
	function getCoord():Vector2 { return _tileReference.getCoord();}
	function getSurfaceCoord():Vector3 
	{ 
		var surfaceCoord = _tileReference.getSurfaceCoord();
		surfaceCoord.y += 0.5;
		return surfaceCoord;
	}
	function getTileReference():Tile { return _tileReference; }
	function isPlayer():boolean { return isPlayerCharacter;}
	//not needed by Enemy
	function getSightRadius():int { return sightRadius; }
	function getMoveRadius():int { return moveRadius;}
	function getEngageRadius():int { return engageRadius; }
	function getAttackRadius():int { return attackRadius; }
	function getAttackDamage():int { return attackDamage; }
	function getActualDamage():int { return actualDamage; }
	//used by Map to check whether need to update Fog Of War
	function hasMoved():boolean { return _hasMoved; }
	function getHealth() { return myHealth; }
	function getTurnsLeft(){ return turnsLeft; }
	function getIsLocked(){ return _isLocked; };
	function getAttackPercent() { return _attackPercent; }
	function getHealthPercent(){ return _healthPercent; }
	function getIsCooldown(){ return _isCooldown; }
	function getSkillRadius(){ return skillRadius; }
	function getEnemyType(){ return _enemyType; }
	function getIsDead(){ return isDead; }
	function getPreviousFlankable():Array { return previousFlankable; }
	function getDecisionState() { return _decisionState; }
	function getPreviousTile():Tile { return _previousTile; }
	//used locally to check whether the character has stopped moving
	//function reachedDestination():boolean { return _destReached;}
	
	
	// BOOLEAN FUNCTIONS
	function isInCover() { return _tileReference.isCover(); }
	function getCoverDirection(direction:int) { return _tileReference.getCoverDirection(direction); }
	function isFlanked(enemy:GameCharacter)
	{
		var directionOfEnemy = enemy.getSurfaceCoord() - this.getSurfaceCoord();
		var isFlanked:boolean = true;
				
		//to the right
		if (directionOfEnemy.x > 0 )
		{
			isFlanked = !_tileReference.getCoverDirection(Tile.COVER_DIRECTION_RIGHT);
			//Debug.Log("Flanked to right:" + isFlanked);
		}
		//to the left
		else if (directionOfEnemy.x < 0 )
		{
			isFlanked = !_tileReference.getCoverDirection(Tile.COVER_DIRECTION_LEFT);
			//Debug.Log("Flanked to left:" + isFlanked);
		}
		
		//if not flanked, return false 	
		if (!isFlanked)
			return false;
		
		//to the top
		if (directionOfEnemy.z > 0)
		{
			isFlanked = !_tileReference.getCoverDirection(Tile.COVER_DIRECTION_TOP);
			//Debug.Log("Flanked to top:" + isFlanked);
		}
		else if (directionOfEnemy.z < 0)
		{
			isFlanked = !_tileReference.getCoverDirection(Tile.COVER_DIRECTION_BOTTOM);
			//Debug.Log("Flanked to bottom:" + isFlanked);
		}
		
		return isFlanked;
	}
	
	
	//SETTERS
	function setWorldCoord():void
	{
		var v3:Vector3 = _tileReference.getSurfaceCoord();
		this.transform.position = v3;
	}
	function setTileReference(tile:Tile):void 
	{
		_tileReference = tile;
		_tileReference.setEnemy(this);
		_tileReference.setIsOccupied(true);
	}
	function setMapReference(map:Map)
	{
		_mapReference = map;
	}
	function setManagerReference(cm:CharacterManager)
	{
		_characterManager = cm;
	}
	function setIsPlayer(b:boolean):void { 
		isPlayerCharacter = b;
	}
	function setSightRadius(r:int):void { sightRadius = r; }
	function setMoveRadius(r:int):void { moveRadius = r; }
	function setHasMoved(b:boolean):void{ _hasMoved = b;}
	function setIsLocked(b:boolean):void{ _isLocked = b; }
	function setNearestCoverTile(cover:Tile):void{ _nearestCoverTile = cover; }
	function setPreviousFlankable(f:Array){ previousFlankable = f; }
	function setDecisionState(decision:DecisionState) { _decisionState = decision; }
	function setPreviousTile(previousTile:Tile){ _previousTile = previousTile; }
	/*function SetIsDone(b:boolean) {
		isDone = b;
	}*/
	
	function setGridTarget(radius:int)
	{
		var aoeGrid = Resources.Load("Grid_target");
		_aoeGrid = Instantiate(aoeGrid);
		_aoeGrid.transform.position = getSurfaceCoord();
		_aoeGrid.transform.position.y -= 0.45;
		radius *= 2;
		radius += 1;
		_aoeGrid.transform.localScale = new Vector3(radius/10.0,0,radius/10.0);
		_aoeGrid.SetActive(false);
	}
	
	function removeGridTarget()
	{
		if (_aoeGrid != null)
			Destroy(_aoeGrid);
		_aoeGrid = null;
	}
	
}